/*
 *  Copyright 2011 Gedare Bloom.
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.com/license/LICENSE.
 *
 *  $Id$
 */

#include <rtems.h>
#include <rtems/libmmu.h>
#include <libcpu/memoryprotection.h>
#include "mmu_support.h"

rtems_status_code _CPU_Memory_protection_Initialize( void )
{
  rtems_interrupt_level  level;

  //sparc64_tlb_print();

  rtems_interrupt_disable( level );

  // FIXME: cannot flush tlb because of firmware entries. repopulating them
  // would be too hard and we already took over the MMU. We could avoid going
  // to the firmware but need to create a complete trap table. We can
  // selectively load/unload TLB entries instead.
  ///tlb_invalidate_all();
  
  // flush cache
  rtems_cache_flush_entire_data();

  sparc64_mmu_initialize();

  rtems_interrupt_enable( level );
  return RTEMS_SUCCESSFUL;
}

rtems_status_code _CPU_Memory_protection_Verify_permission(
    rtems_memory_protection_permission permission
) {
  return RTEMS_SUCCESSFUL;
}

rtems_status_code _CPU_Memory_protection_Verify_size(
    size_t size
) {
  if ( size & 0x1FFF )
    return RTEMS_INVALID_SIZE;
  return RTEMS_SUCCESSFUL;
}

rtems_status_code _CPU_Memory_protection_Uninstall_MPE(
    rtems_memory_protection_entry *mpe
) {
  void *base;
  size_t bounds;
  int num_pages;
  int i;
  bool r,x;
  int pagesize = SPARC64_PAGE_SIZE;

  base = mpe->region.base;
  bounds = mpe->region.bounds;

  num_pages = (bounds + (pagesize-1))/pagesize;
  if ( (uintptr_t)base & (pagesize-1) )
    num_pages++;

  r = rtems_memory_protection_has_read_permission(mpe);
  x = rtems_memory_protection_has_execute_permission(mpe);

  for ( i = 0; i < num_pages; i++ ) {
    if (r) {
      dtlb_demap(TLB_DEMAP_PAGE,TLB_DEMAP_PRIMARY,base + i * pagesize);
    }
    if (x) {
      itlb_demap(TLB_DEMAP_PAGE,TLB_DEMAP_PRIMARY,base + i * pagesize);
    }
  }
  return RTEMS_SUCCESSFUL;
}

rtems_status_code _CPU_Memory_protection_Install_MPE(
    rtems_memory_protection_entry *mpe
) {
  void *base;
  size_t bounds;
  int num_pages;
  bool r,w,x,c;
  int i;
  int pagesize = 0x2000;  // 8K

  base = mpe->region.base;
  bounds = mpe->region.bounds;

  num_pages = (bounds + (pagesize-1))/pagesize;
  if ( (uintptr_t)base & (pagesize-1) )
    num_pages++;

  r = rtems_memory_protection_has_read_permission(mpe);
  w = rtems_memory_protection_has_write_permission(mpe);
  x = rtems_memory_protection_has_execute_permission(mpe);
  c = rtems_memory_protection_has_cacheable_permission(mpe);

  for ( i = 0; i < num_pages; i++ ) {
    /* add to dtlb if read or write permission is set */
    if ( r ) {
      sparc64_dtlb_insert(
          base + i * pagesize,
          p_to_f(base + i * pagesize),
          PAGESIZE_8K,
          0,
          w,
          c,
          false
      );
    } else if ( w ) {
//      printk("write permission set without read permission\n");
      return RTEMS_INTERNAL_ERROR;
    }

    /* add to itlb if execute permission is set */
    if ( x ) {
      sparc64_itlb_insert(
          base + i * pagesize,
          p_to_f(base + i * pagesize),
          PAGESIZE_8K,
          0,
          c,
          false
      );
    }
  }

  return RTEMS_SUCCESSFUL;
}

